/**
 *
 */
package co.fitstart.payment.web.controller.alipay;

import java.math.BigDecimal;
import java.util.Date;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import com.alipay.api.AlipayApiException;
import com.alipay.api.internal.util.AlipaySignature;
import com.demo2do.core.utils.DateFormatUtils;
import com.demo2do.core.utils.DateUtils;
import com.demo2do.core.web.utils.WebUtils;

import co.fitstart.payment.Constant;
import co.fitstart.payment.cache.accessor.PersistentAccessor;
import co.fitstart.payment.entity.app.App;
import co.fitstart.payment.entity.app.AppSetting;
import co.fitstart.payment.entity.channel.Channel;
import co.fitstart.payment.entity.request.RequestRecord;
import co.fitstart.payment.entity.transaction.TransactionRecord;
import co.fitstart.payment.entity.transaction.TransactionStatus;
import co.fitstart.payment.service.AppService;
import co.fitstart.payment.service.ChannelService;
import co.fitstart.payment.service.RequestRecordService;
import co.fitstart.payment.service.TransactionRecordService;
import co.fitstart.payment.support.NotificationResult;
import co.fitstart.payment.web.handler.OrderHandler;

/**
 * @author lute
 */
@RequestMapping("/alipay")
@Controller
public class AlipayCallbackController {

    private static final Logger logger = LoggerFactory.getLogger(AlipayCallbackController.class);

    @Autowired
    private PersistentAccessor accessor;

    @Autowired
    private AppService appService;

    @Autowired
    private ChannelService channelService;

    @Autowired
    private RequestRecordService requestRecordService;

    @Autowired
    private TransactionRecordService transactionRecordService;

    @Autowired
    private OrderHandler orderHandler;

    @ResponseBody
    @RequestMapping(value = "/callback", method = RequestMethod.POST)
    public String callback(HttpServletRequest httpServletRequest) {
        Map<String, String> params = WebUtils.extractPostParameters(httpServletRequest);

        // Get out trade number
        String outTradeNo = params.get("out_trade_no");

        // Get request record according to out trade number
        RequestRecord requestRecord = requestRecordService.getRequestRecord(outTradeNo);
        if (requestRecord == null) {
            logger.error("AlipayCallbackController#callback - Can not find request record with request number [{}].", outTradeNo);
            return NotificationResult.FAIL.getAlias();
        }

        // Get app according to app id in request record
        App app = appService.load(requestRecord.getAppId());
        // Get app settings
        Map<String, AppSetting> appSettings = accessor.getAppSettings().get(app.getId());
        String publicKey = appSettings.get(Constant.SETTING_ALI_PUBLIC_KEY).getValue();

        // Check signature
        try {
            AlipaySignature.rsaCheckV1(params, publicKey, "utf-8");
        } catch (AlipayApiException e) {
            logger.error("AlipayCallbackController#callback - Check request parameters signature encounters exception: {}", e);
            return NotificationResult.FAIL.getAlias();
        }

        // Get transaction record and channel
        TransactionRecord transactionRecord = transactionRecordService.getTransactionRecord(outTradeNo);
        Channel channel = channelService.load(requestRecord.getChannelId());
        
        // Check total fee with transaction record amount
        if (transactionRecord == null) {
            logger.error("AlipayCallbackController#callback - Transaction record is empty.");
            return NotificationResult.FAIL.getAlias();
        }

        // Get relevant data from params
        String sellerId = params.get("seller_id");
        String tradeNo = params.get("trade_no");
        Date payTime = DateUtils.parseDate(params.get("gmt_payment"), "yyyy-MM-dd HH:mm:ss");
        int totalFee = new BigDecimal(params.get("total_amount")).multiply(new BigDecimal(100)).intValue();

        // Check Alipay seller id
        String storedSellerId = appSettings.get(Constant.SETTING_ALI_SELLERID).getValue();
        if (!StringUtils.equals(sellerId, storedSellerId)) {
            logger.error("AlipayCallbackController#callback - Seller id [{}] and stored seller id [{}] are not match.", sellerId, storedSellerId);
            return NotificationResult.FAIL.getAlias();
        }

        // Check total fee with transaction record amount
        if (totalFee != transactionRecord.getAmount()) {
            logger.error("AlipayCallbackController#callback - Transaction record amount [{}] and returned total amount [{}] are not match.", transactionRecord.getAmount(), totalFee);
            return NotificationResult.FAIL.getAlias();
        }

        // transaction record is alerday paid, just return scuesss
        if (transactionRecord.getTransactionStatus().isPaid()) {
            logger.trace("AlipayCallbackController#callback - Transaction record is alerday paid, just return success.");
            return NotificationResult.SUCCESS.getAlias();
        }

        // transaction record is created, sync order then udpate transaction record to piad status
        if (transactionRecord.getTransactionStatus().isCreated()) {
            
            boolean isOrderSync = orderHandler.synchronizeOrderStatus(app, requestRecord, channel, totalFee, DateFormatUtils.format(payTime, "yyyyMMddHHmmss"));
            
            if (isOrderSync) {  // sync order success
                transactionRecordService.changeTransactionStatus(transactionRecord, TransactionStatus.PAID, tradeNo);
                return NotificationResult.SUCCESS.getAlias();
            
            } else {            // sync order failed
                logger.error("AlipayCallbackController#callback - Synchronizing order fails.");
            }
        }

        return NotificationResult.FAIL.getAlias();
    }

}
